
#include "Scene.hpp"
#include "Color.hpp"
#include "Math.hpp"
#include <iostream>
#include <fstream>
#include <cmath>

#include <FreeImagePlus.h>

using namespace com::toxiclabs::tsoc;
using namespace std;

Camera::Camera()
{
	Vector position(0.0f,0.0f,-5.0f,1.0f);
	Vector target(0.0f,0.0f,0.0f,1.0f);
	
	angle=0.0f;
	near=0.0f;
	far=10000.0f;
	
	name="default";
	
	CreateMatrix();
}

Camera::Camera(string & name,Vector & position,Vector & target,float angle,float near,float far)
{
	this->name=name;
	this->position=position;
	this->target=target;
	this->angle=angle;
	this->near=near;
	this->far=far;	
	

	CreateMatrix();
	
}


void Camera::CreateMatrix()
{
	
	Vector forward;
	Vector up;
	Vector right;
	
	
	
	forward = target - position;
	forward.Normalize();
	forward.Negate();
	
	
	up.x=std::sin(DegToRad(angle));
	up.y=std::cos(DegToRad(angle));
	up.z=0.0f;
	up.w=0.0f;
	
	right = forward ^ up;
	right.Normalize();
	
	Matrix mT;
	Matrix mR;
	
	mT = Matrix::CreateTranslation(-position.x,-position.y,-position.z);
		
	
	up = right ^ forward;	
	up.Normalize();
	
	mR = Matrix::CreateIdentity();
	
	//right
	mR.data[0]=right.x;
	mR.data[4]=right.y;
	mR.data[8]=right.z;
	mR.data[12]=0.0f;
	
	//up
	mR.data[1]=up.x;
	mR.data[5]=up.y;
	mR.data[9]=up.z;
	mR.data[13]=0.0f;
	
	//forward
	mR.data[2]=-forward.x;
	mR.data[6]=-forward.y;
	mR.data[10]=-forward.z;
	mR.data[14]=0.0f;
	
	mR.data[3]=0.0f;
	mR.data[7]=0.0f;
	mR.data[11]=0.0f;
	mR.data[15]=1.0f;
	
		
	matrix = mR*mT;
	
}

Texture::Texture()
{
}

Texture * Texture::Load(const char * filename)
{
	fipImage img;
	Texture * ret;
	
	if(img.load(filename))
	{
		ret = new Texture();
		ret->width=img.getWidth();
		ret->height=img.getHeight();
		
		cout<<"* Loading texture: "<<filename<<"("<<ret->width<<"x"<<ret->height<<")"<<endl;
		
		ret->data=new uint8_t[ret->width*ret->height*3];
		
		for(int j=0;j<ret->height;j++)
		{
			for(int i=0;i<ret->width;i++)
			{
				RGBQUAD value;
				img.getPixelColor(i,j,&value);
				
				int offset=(3*i)+(3*j*ret->width);
				
				ret->data[offset+0]=value.rgbRed;
				ret->data[offset+1]=value.rgbGreen;
				ret->data[offset+2]=value.rgbBlue;
			}
		}
		
	}
	else
	{
		cerr<<"Error loading texture: "<<filename<<endl;
	}
	
	return ret;
	
	
}

/*
* This code should be checked
*/
Color Texture::Texel(float u,float v)
{
	if(u<0.0)u=1.0f+u;
	if(v<0.0)v=1.0f+v;
	u = u * width - 0.5f;
	v = v * height - 0.5f;
	int x = std::floor(u);
	int y = std::floor(v);
	float u_ratio = u - x;
	float v_ratio = v - y;
	float u_opposite = 1.0f - u_ratio;
	float v_opposite = 1.0f - v_ratio;
	
	if(x<0)x=width-x;
	if(y<0)y=height-y;
	
	x=x%(width-1);
	y=y%(height-1);
	
	
		
	int offset = (x*3)+(y*width*3);
	int offset2 = ((x+1)*3)+(y*width*3);
	int offset3 = (x*3)+((y+1)*width*3);
	int offset4 = ((x+1)*3)+((y+1)*width*3);
	
	Color c;
	
	c.r=((data[offset+0] * u_opposite + data[offset2+0] * u_ratio) * v_opposite + (data[offset3+0] * u_opposite + data[offset4+0]*u_ratio)*v_ratio)/256.0f;
	c.g=((data[offset+1] * u_opposite + data[offset2+1] * u_ratio) * v_opposite + (data[offset3+1] * u_opposite + data[offset4+1]*u_ratio)*v_ratio)/256.0f;
	c.b=((data[offset+2] * u_opposite + data[offset2+2] * u_ratio) * v_opposite + (data[offset3+2] * u_opposite + data[offset4+2]*u_ratio)*v_ratio)/256.0f;
	c.a=1.0f;
	
	return c;
}

Vector Texture::Bump(float u,float v)
{
	if(u<0.0)u=1.0f+u;
	if(v<0.0)v=1.0f+v;
	u = u * width - 0.5f;
	v = v * height - 0.5f;
	int x = std::floor(u);
	int y = std::floor(v);
	
	if(x<0)x=width-x;
	if(y<0)y=height-y;
	
	x=x%(width-1);
	y=y%(height-1);
	
	int offset = (x*3)+(y*width*3);
	
	Vector r;
	
	r.x = (data[offset + 0]*2.0f )-1.0f;
	r.y = (data[offset + 1]*2.0f )-1.0f;
	r.z = (data[offset + 2]*2.0f )-1.0f;
	r.w=0.0f;
	
	return r;
}

Material::Material()
{
	diffuse.Set(0.9f,0.9f,0.9f,1.0f);
	specular.Set(0.9f,0.9f,0.9f,1.0f);
		
	flags = 0;
}

Scene::Scene()
{

}

/**
 * Scene stuff will be free here
 **/ 
Scene::~Scene()
{
	
}


void Scene::ApplyCamera()
{
	
	
	if(cameras.size()==0)
	{
		cameras.push_back(new Camera());
	}
	
	Camera * camera = cameras[0];
	
	Matrix & mC = camera->matrix;

	for(int n=0;n<triangles.size();n++)
	{
		
		triangles[n]->vertex[0]=triangles[n]->vertex[0] * mC;
		triangles[n]->vertex[1]=triangles[n]->vertex[1] * mC;
		triangles[n]->vertex[2]=triangles[n]->vertex[2] * mC;
		
		triangles[n]->vertex[0].Homogeneus();
		triangles[n]->vertex[1].Homogeneus();
		triangles[n]->vertex[2].Homogeneus();
		
		triangles[n]->normal[0]=triangles[n]->normal[0] * mC;
		triangles[n]->normal[1]=triangles[n]->normal[1] * mC;
		triangles[n]->normal[2]=triangles[n]->normal[2] * mC;
		
		triangles[n]->pnormal=triangles[n]->pnormal * mC;
						
	}
	
	
}


Light::Light()
{
	q_direction=gsl_qrng_alloc (gsl_qrng_sobol, 2);
	q_position=gsl_qrng_alloc (gsl_qrng_sobol, 2);
}

Light::~Light()
{
	gsl_qrng_free(q_direction);
	gsl_qrng_free(q_position);
}
